package com.colabo.j2ee.web.core.util;

import java.math.BigDecimal;

/**
 * mathimatical process for numeric variables
 * 
 * @author Anthony Do
 * @since 2009-11-2
 */
public final class MathUtil {
	
	// default accuracy for division
	private static final int DEFAULT_DIV_SCALE = 10;
	
	// instanciation is forbidden
	private MathUtil() {
	}
	
	/**
	 * To see if a number is not greater than another one
	 * @param arg0 
	 * 				param 1
	 * @param arg1
	 * 				param 2
	 * @return <code>boolean</code>: <code>true</code> if param1 <= param2; <code>false</code> if param1 > param2;
	 */
	public static final boolean isDoubleNotGreaterThan(double arg0, double arg1) {
		boolean flag = false;
		BigDecimal b1 = new BigDecimal(Double.toString(arg0));
		BigDecimal b2 = new BigDecimal(Double.toString(arg1));
		
		if (b1.compareTo(b2) <= 0) {
			flag = true;
		} else {
			flag = false;
		}

		return flag;
	}
	
	/**
	 * To see if a number is not less than another one
	 * 
	 * @param arg0 
	 * 				param 1
	 * @param arg1
	 * 				param 2
	 * @return <code>boolean</code>: <code>true</code> if param1 >= param2; <code>false</code> if param1 < param2;
	 */
	public static final boolean isDoubleNotLessThan(double arg0, double arg1) {
		boolean flag = false;
		BigDecimal b1 = new BigDecimal(Double.toString(arg0));
		BigDecimal b2 = new BigDecimal(Double.toString(arg1));

		if (b1.compareTo(b2) >= 0) {
			flag = true;
		} else {
			flag = false;
		}

		return flag;
	}
	
	/**
	 * To see if a number is equal to another one
	 * 
	 * @param arg0 
	 * 				param 1
	 * @param arg1
	 * 				param 2
	 * @return <code>boolean</code>: <code>true</code> if param1 = param2; <code>false</code> if param1 <> param2;
	 */
	public static boolean isDoubleEqualTo(double arg0, double arg1) {
		boolean flag = false;
		BigDecimal b1 = new BigDecimal(Double.toString(arg0));
		BigDecimal b2 = new BigDecimal(Double.toString(arg1));

		if (b1.compareTo(b2) == 0) {
			flag = true;
		} else {
			flag = false;
		}

		return flag;
	}
	
	/**
	 * accuratly addition two <code>double</code> variables;
	 * 
	 * @param arg0
	 *            param 1	
	 * @param arg1
	 *            param 2
	 * @return <code>double</code>: arg0 + arg1
	 */
	public static double add(double arg0, double arg1) {
		BigDecimal b1 = new BigDecimal(Double.toString(arg0));
		BigDecimal b2 = new BigDecimal(Double.toString(arg1));
		return b1.add(b2).doubleValue();
	}
	
	/**
	 * accuratly subtraction two <code>double</code> variables;
	 * 
	 * @param minuend
	 * @param subtrahend
	 * @return <code>double</code>： minuend - subtrahend
	 */
	public static double sub(double minuend, double subtrahend) {
		BigDecimal b1 = new BigDecimal(Double.toString(minuend));
		BigDecimal b2 = new BigDecimal(Double.toString(subtrahend));
		return b1.subtract(b2).doubleValue();
	}

	/**
	 * accuratly multiply two <code>double</code> variables;
	 * 
	 * @param multiplicand            
	 * @param multiplier
	 * 
	 * @return <code>double</code>：multiplicand * multiplier
	 */
	public static double mul(double multiplicand, double multiplier) {
		BigDecimal b1 = new BigDecimal(Double.toString(multiplicand));
		BigDecimal b2 = new BigDecimal(Double.toString(multiplier));
		return b1.multiply(b2).doubleValue();
	}

	/**
	 * accuratly division two <code>double</code> variables;
	 * 
	 * @param dividend
	 * @param divisor
	 * @param scale
	 * 				accuracy of the result            
	 * @return <code>double</code>：round (dividend / divisor) with the specified scale
	 * @throws <code>IllegalArgumentException</code>
	 * 				if scale < 0
	 * @see #ROUND_HALF_UP
	 */
	public static double div(double dividend, double divisor, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException("The scale must be a positive integer or zero");
		}
		BigDecimal b1 = new BigDecimal(Double.toString(dividend));
		BigDecimal b2 = new BigDecimal(Double.toString(divisor));
		return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * accuratly division two <code>double</code> variables, and round the result with the default
	 * scale
	 * 
	 * @param dividend
	 * @param divisor
	 * 
	 * @return <code>double</code>：round (dividend / divisor) with the default scale
	 * @see #DEFAULT_DIV_SCALE
	 * @see #div
	 */
	public static double div(double dividend, double divisor) {
		return div(dividend, divisor, DEFAULT_DIV_SCALE);
	}

	/**
	 * round the given double number with the given scale
	 * 
	 * @param d
	 *            number needed to be rounded
	 * @param scale
	 *            given scale
	 * @return <code>double</code>：round(d) with given scale
	 * @throws <code>IllegalArgumentException</code>
	 * 				if scale < 0
	 */
	public static double round(double d, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException("The scale must be a positive integer or zero");
		}
		BigDecimal b = new BigDecimal(Double.toString(d));
		BigDecimal one = new BigDecimal("1");
		// invoke the divide method of BigDecimal.
		return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

}
